package com.whz.generic.threads;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;

/**
 * 简单设置的通用线程池
 *
 * @author whz
 */
@Slf4j
public final class DefaultCommonThreadPool {
    public static final DefaultCommonThreadPool DEFAULT_THREAD_POOL = new DefaultCommonThreadPool();
    /**
     * 线程空闲的最大时间,与task提交速率相关,单位s
     */
    private static final long KEEP_ALIVE_TIME = 45L;
    private static final String TONGYU_COMMON_THREAD_POOL = "common-thread-pool";
    private static final String TONGYU_SCHEDULED_THREAD_POOL = "scheduled-thread-pool";
    private static final ThreadPoolExecutor.AbortPolicy DEFAULT_ABORT_POLICY = new AbortPolicy() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            log.error("Task " + r.toString() +
                      " rejected from " +
                      e.toString());
        }
    };
    /**
     * 最小线程数1
     */
    private static final int NUM_OF_MIN = 1;
    private static final int NUM_OF_MAX = Math.max(Runtime.getRuntime().availableProcessors() >> 1, 2);
    private static final int QUEUE_SIZE = 200;

    public static ThreadFactory buildThreadFactory(final String prefix) {
        String name = prefix;
        if (!StringUtils.hasLength(name)) {
            name = "generic-thread-pool";
        }
        // thread应该有一个有意义的名字
        return new ThreadFactoryBuilder()
                .setNameFormat(name + "-%d")
                .setDaemon(true)
                .build();
    }


    /**
     * 创建指定队列长队的线程池
     *
     * @return 线程池
     */
    public ExecutorService defaultAsyncExecutorService() {
        return defaultAsyncExecutorService(TONGYU_COMMON_THREAD_POOL,
                                           NUM_OF_MIN,
                                           NUM_OF_MAX,
                                           KEEP_ALIVE_TIME,
                                           QUEUE_SIZE);
    }

    /**
     * 创建通用线程池
     *
     * @param prefix     现成前缀
     * @param coreNum    最小线程数
     * @param maxNum     最大线程数
     * @param keepLiveMs 线程最大空闲时间
     * @param queueSize  任务队列长度
     * @return 执行器
     */
    public ExecutorService defaultAsyncExecutorService(final String prefix, final int coreNum, final int maxNum,
                                                       final long keepLiveMs, final int queueSize) {
        String name = StringUtils.hasText(prefix)? prefix : TONGYU_COMMON_THREAD_POOL;
        log.info("===== created ExecutorService with prefix Name:{}", name);
        return new ThreadPoolExecutor(
                coreNum,
                maxNum,
                keepLiveMs,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(queueSize > 0? queueSize : QUEUE_SIZE),
                buildThreadFactory(StringUtils.hasText(prefix)? prefix : TONGYU_COMMON_THREAD_POOL),
                DEFAULT_ABORT_POLICY);
    }

    /**
     * 创建默认参数的线程池
     *
     * @return 线程池
     */
    public ScheduledExecutorService scheduledExecutorService() {
        return scheduledExecutorService(TONGYU_SCHEDULED_THREAD_POOL, NUM_OF_MIN, NUM_OF_MAX);
    }

    /**
     * 创建定时重复触发任务的线程池,该线程池会创建最大线程数来处理任务,CPU会100%;
     * 优先级队列长度无限
     *
     * @param prefix 线程名前缀
     * @return 定时线程池
     */
    public ScheduledExecutorService scheduledExecutorService(final String prefix,
                                                             final int coreNum,
                                                             final int maxNum) {

        String name = StringUtils.hasText(prefix)? prefix : TONGYU_SCHEDULED_THREAD_POOL;
        ScheduledThreadPoolExecutor service = new ScheduledThreadPoolExecutor(
                coreNum,
                buildThreadFactory(name),
                DEFAULT_ABORT_POLICY);

        service.setMaximumPoolSize(maxNum);
        log.info("===== created ScheduledExecutorService with prefix Name:{}", name);
        return service;
    }
}